﻿using jvm.classfile;
using jvm.classfile.attributeinfo;
using jvm.mm;
using jvm.rtda.frame;
using jvm.rtda.heap.clazz;
using jvm.rtda.heap.clazz.clazz;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace jvm.rtda.heap
{
    partial class JClass
    {
        public ushort accessFlags;
        public string name;
        public string superClassName;
        public string[] interfaceNames;
        public JConstantPool constantPool;
        public JField[] fields;
        public JMethod[] methods;
        public string sourceFile;
        // 额外信息
        public JClassLoader loader;
        public JClass superClass;
        public JClass[] interfaces;
        public uint instanceSlotCount; // 非静态变量的数量，存在Object里
        public uint staticSlotCount;
        public Slots staticVars; // 存在方法区
        public bool inited;
        public JObject jLClassObject; // java/lang/Class实例

        public JClass()
        {
            // 数组类、基本类等组装的class
        }

        public JClass(ClassFile classFile)
        {
            accessFlags = classFile.access_flags;
            name = classFile.ClassName();
            superClassName = classFile.SuperClassName();
            interfaceNames = classFile.InterfaceNames();
            constantPool = new JConstantPool(this, classFile.constantPool);
            fields = new JField[classFile.fields.Length];
            for (int i = 0; i < classFile.fields.Length; i++)
            {
                fields[i] = new JField(this, classFile.fields[i]);
            }
            methods = new JMethod[classFile.methods.Length];
            for (int i = 0; i < classFile.methods.Length; i++)
            {
                methods[i] = new JMethod(this, classFile.methods[i]);
            }
            SourceFileAttribute sourceFileAttribute = classFile.SourceFileAttribute;
            sourceFile = sourceFileAttribute == null ? "unknown" : sourceFileAttribute.FileName;
        }

        public JObject NewObject()
        {
            JObject obj = new JObject(this);
            return MemoryManager.Alloc(obj);
        }

        public string PackageName()
        {
            return name.Substring(0, name.LastIndexOf("/"));
        }

        public string JavaName
        {
            get{ return name.Replace("/", "."); }
        }

        public bool Is(ushort flag)
        {
            return (accessFlags & flag) != 0;
        }

        public bool IsAccessibleTo(JClass other)
        {
            return Is(AccessFlags.ACC_PUBLIC) || PackageName().Equals(other.PackageName());
        }

        public JMethod FindMainMethod()
        {
            return FindStaticMethod("main", "([Ljava/lang/String;)V");
        }

        public JMethod FindClinitMethod()
        {
            return FindStaticMethod("<clinit>", "()V");
        }

        public JMethod FindInstanceMethod(string name, string descriptor)
        {
            return FindMethod(name, descriptor, false);
        }

        public JMethod FindStaticMethod(string name, string descriptor)
        {
            return FindMethod(name, descriptor, true);
        }

        JMethod FindMethod(string name, string descriptor, bool isStatic)
        {
            foreach (JMethod method in methods)
            {
                if (method.Is(AccessFlags.ACC_STATIC) == isStatic && method.name.Equals(name) &&
                    method.descriptor.Equals(descriptor))
                {
                    return method;
                }
            }
            return null;
        }

        public JField FindField(string name, string descriptor)
        {
            foreach (JField field in fields)
            {
                if (field.name.Equals(name) && field.descriptor.Equals(descriptor))
                {
                    return field;
                }
            }
            foreach (JClass iface in interfaces)
            {
                JField field = iface.FindField(name, descriptor);
                if (field != null)
                {
                    return field;
                }
            }

            if (superClass != null)
            {
                return superClass.FindField(name, descriptor);
            }

            return null;
        }

        public JClass ArrayClass()
        {
            string arrayName = ClassNameHelper.GetArrayClassName(name);
            return loader.LoadClass(arrayName);
        }

        public bool IsJLObject()
        {
            return name.Equals("java/lang/Object"); ;
        }

        public bool IsJLCloneable()
        {
            return name.Equals("java/lang/Cloneable"); ;
        }

        public bool IsJLSerializable()
        {
            return name.Equals("java/lang/Serializable"); ;
        }

        public JObject GetFieldObject(string name, string descriptor)
        {
            JField field = FindField(name, descriptor);
            return staticVars.GetRef(field.slotId);
        }

        public void SetFieldObject(string name, string descriptor, JObject obj)
        {
            JField field = FindField(name, descriptor);
            staticVars.SetRef(field.slotId, obj);
        }
    }
}
